﻿//-----------------------------------------------------------------------
// <copyright file="Form1.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings

using System;
using System.Data.SqlClient;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using Altova.AltovaXML;
using GateShift.Common.Controls.Wizards;
using GateShift.Tools.DatabaseMigration.Configuration;
using GateShift.Tools.DatabaseMigration.Utilities;

#endregion

namespace GateShift.Tools.DatabaseMigration
{
    /// <summary>
    /// Wizard Form for database migration configuration
    /// </summary>
    public partial class Form1 : Form
    {
        private SqlConnectionStringBuilder connectionStringBuilder;
        private VersionConfig changeSetList;
        private int currentChangeSet;
        private int latestChangeSet;

        /// <summary>
        /// Initializes a new instance of the Form1 class
        /// </summary>
        public Form1()
        {
            InitializeComponent();

            changeSetList = new VersionConfig();

            connectionStringBuilder = new SqlConnectionStringBuilder(Properties.Settings.Default.CurrentConnectionString);

            // If this is not in configuration, default to integrated security
            if (string.IsNullOrEmpty(Properties.Settings.Default.CurrentConnectionString))
            {
                connectionStringBuilder.IntegratedSecurity = true;
            }
        }

        /// <summary>
        /// Event handler for the form loading
        /// </summary>
        /// <param name="sender">Form calling this method</param>
        /// <param name="e">Empty EventArgs</param>
        private void Form1_Load(object sender, EventArgs e)
        {
        }

        #region Preparation Page

        #region Preparation Page Methods

        /// <summary>
        /// Loads all content for the preparation page
        /// </summary>
        private void LoadPreparationPage()
        {
            currentChangeSet = SqlUtilities.GetCurrentChangeSet(connectionStringBuilder.ConnectionString);

            LoadXmlChangeSet();
            CheckLatestChangeSet();
            ReduceChangeSetsToApply();

            BindPreparationControls();
        }

        /// <summary>
        /// Binds all content for the preparation page
        /// </summary>
        private void BindPreparationControls()
        {
            lblCurrentChangeSetValue.Text = currentChangeSet.ToString();
            lblLatestChangeSetValue.Text = latestChangeSet.ToString();
            LoadXslChangeSetDescription();
        }

        /// <summary>
        /// Reduces the list of changesets already applied from the list of available updates
        /// </summary>
        private void ReduceChangeSetsToApply()
        {
            DBVersionCollection versionsToRemove = new DBVersionCollection();

            foreach (DBVersion item in changeSetList.Versions)
            {
                ChangeSetCollection itemsToRemove = new ChangeSetCollection();

                foreach (ChangeSet change in item.ChangeSets)
                {
                    if (Convert.ToInt32(change.Number) < currentChangeSet || change.Database.ToLower() != "gateshift")
                    {
                        itemsToRemove.Add(change);
                    }
                }

                foreach (ChangeSet change in itemsToRemove)
                {
                    item.ChangeSets.Remove(change);
                }

                if (item.ChangeSets.Count <= 0)
                {
                    versionsToRemove.Add(item);
                }
            }

            foreach (DBVersion item in versionsToRemove)
            {
                changeSetList.Versions.Remove(item);
            }
        }

        /// <summary>
        /// Loads the list of changesets that are available
        /// </summary>
        private void LoadXmlChangeSet()
        {
            XmlSerializer s = new XmlSerializer(typeof(VersionConfig));

            using (FileStream fileStream = new FileStream(Properties.Settings.Default.DatabaseChangesetFile, FileMode.Open))
            {
                changeSetList = (VersionConfig)s.Deserialize(fileStream);
            }
        }

        /// <summary>
        /// Finds the latest changeset that could be applied
        /// </summary>
        private void CheckLatestChangeSet()
        {
            latestChangeSet = 0;
            foreach (DBVersion item in changeSetList.Versions)
            {
                foreach (ChangeSet change in item.ChangeSets)
                {
                    if (Convert.ToInt32(change.Number) > latestChangeSet)
                    {
                        latestChangeSet = Convert.ToInt32(change.Number);
                    }
                }
            }
        }

        /// <summary>
        /// Gets the list of changesets to apply
        /// </summary>
        /// <returns>New XML representation of the changeset file</returns>
        private string GetChangeSetsToApply()
        {
            XmlSerializer s = new XmlSerializer(typeof(VersionConfig));
            StringWriter writer = new StringWriter();

            s.Serialize(writer, changeSetList);

            return writer.GetStringBuilder().ToString();
        }

        /// <summary>
        /// Use the XSLT transform to create a visual description for all the changesets that will be applied
        /// </summary>
        private void LoadXslChangeSetDescription()
        {
            ApplicationClass appXML = new ApplicationClass();

            appXML.XSLT2.InputXMLFromText = GetChangeSetsToApply();
            appXML.XSLT2.XSLFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configuration\\ChangeSets.xslt");
            wbChangeSetHistory.DocumentText = appXML.XSLT2.ExecuteAndGetResultAsString();
        }

        #endregion

        #endregion

        #region Connection Page

        #region Connection Page Methods

        /// <summary>
        /// Loads all content for the connection page
        /// </summary>
        private void LoadConnectionPage()
        {
            // Bind drop downs
            cboConnectServers.Items.AddRange(SqlUtilities.RetrieveSqlInstances().ToArray());

            // Set Controls
            BindConnectionControls();

            // Now check all bindings
            SetConnectionAuthentication();
            CheckConnectionDropDowns();

            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Binds all content for the connection page
        /// </summary>
        private void BindConnectionControls()
        {
            rdoConnectWindows.Checked = connectionStringBuilder.IntegratedSecurity;
            rdoConnectSQL.Checked = !rdoConnectWindows.Checked;
            cboConnectDatabase.Text = connectionStringBuilder.InitialCatalog;
            cboConnectServers.Text = connectionStringBuilder.DataSource;
            pnlConnectSQLAuthentication.Enabled = rdoConnectSQL.Checked;
            txtConnectSQLUser.Text = connectionStringBuilder.UserID;
            txtConnectSQLPassword.Text = connectionStringBuilder.Password;
        }

        /// <summary>
        /// Configures the authentication for the SQL connection
        /// </summary>
        private void SetConnectionAuthentication()
        {
            pnlConnectSQLAuthentication.Enabled = rdoConnectSQL.Checked;
            connectionStringBuilder.IntegratedSecurity = rdoConnectWindows.Checked;

            if (!rdoConnectSQL.Checked)
            {
                return;
            }

            connectionStringBuilder.UserID = txtConnectSQLUser.Text;
            connectionStringBuilder.Password = txtConnectSQLPassword.Text;
        }

        /// <summary>
        /// Checks the states for the connection page controls
        /// </summary>
        private void CheckConnectionDropDowns()
        {
            cboConnectDatabase.Enabled = !string.IsNullOrEmpty(cboConnectServers.Text);
        }

        /// <summary>
        /// Checks whether or not the user can continue past the connection page
        /// </summary>
        private void CheckCanConnectionClickNext()
        {
            bool canContinue = true;

            if (rdoConnectSQL.Checked && (string.IsNullOrEmpty(txtConnectSQLPassword.Text) || string.IsNullOrEmpty(txtConnectSQLUser.Text)))
            {
                canContinue = false;
            }

            if (string.IsNullOrEmpty(cboConnectDatabase.Text))
            {
                canContinue = false;
            }

            if (string.IsNullOrEmpty(cboConnectServers.Text))
            {
                canContinue = false;
            }

            SqlConnection testConnection = new SqlConnection(connectionStringBuilder.ConnectionString);
            try
            {
                testConnection.Open();
                testConnection.Close();
            }
            catch
            {
                canContinue = false;
            }

            wizardForm1.WizardButtons = canContinue ? WizardsButtons.AllShort : WizardsButtons.AllShortNextDisabled;
        }

        /// <summary>
        /// Saves the current connection page selections to a config file
        /// </summary>
        private void SaveCurrentConnectionOptions()
        {
            Properties.Settings.Default.CurrentConnectionString = connectionStringBuilder.ConnectionString;
            Properties.Settings.Default.Save();
        }

        #endregion

        #region Connection Page Events

        /// <summary>
        /// Event handler for the server list changing
        /// </summary>
        /// <param name="sender">ComboBox calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectServers_TextChanged(object sender, EventArgs e)
        {
            CheckConnectionDropDowns();
            connectionStringBuilder.DataSource = cboConnectServers.Text;
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for the Windows Auth Mode
        /// </summary>
        /// <param name="sender">Radio calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectWindows_CheckedChanged(object sender, EventArgs e)
        {
            SetConnectionAuthentication();
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for the SQL Auth Mode
        /// </summary>
        /// <param name="sender">Radio calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectSQL_CheckedChanged(object sender, EventArgs e)
        {
            SetConnectionAuthentication();
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for the database list changing
        /// </summary>
        /// <param name="sender">ComboBox calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectDatabase_DropDown(object sender, EventArgs e)
        {
            cboConnectDatabase.Items.Clear();
            cboConnectDatabase.Items.AddRange(SqlUtilities.RetrieveDatabaseList(connectionStringBuilder.ConnectionString).ToArray());
        }

        /// <summary>
        /// Event handler for the username textbox changing
        /// </summary>
        /// <param name="sender">ComboBox calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectSQLUser_TextChanged(object sender, EventArgs e)
        {
            if (connectionStringBuilder.IntegratedSecurity)
            {
                return;
            }

            connectionStringBuilder.UserID = txtConnectSQLUser.Text;
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for the password textbox changing
        /// </summary>
        /// <param name="sender">TextBox calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectSQLPassword_TextChanged(object sender, EventArgs e)
        {
            if (connectionStringBuilder.IntegratedSecurity)
            {
                return;
            }

            connectionStringBuilder.UserID = txtConnectSQLPassword.Text;
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for advanced connection options being clicked
        /// </summary>
        /// <param name="sender">Button calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectAdvanced_Click(object sender, EventArgs e)
        {
            SqlConnectionForm frmAdvanced = new SqlConnectionForm { ConnectionString = connectionStringBuilder };

            if (frmAdvanced.ShowDialog(this) != DialogResult.OK)
            {
                return;
            }

            connectionStringBuilder = frmAdvanced.ConnectionString;
            BindConnectionControls();
            CheckConnectionDropDowns();
            CheckCanConnectionClickNext();
        }

        /// <summary>
        /// Event handler for the database textbox changing
        /// </summary>
        /// <param name="sender">ComboBox calling this event</param>
        /// <param name="e">Empty EventArgs</param>
        private void ConnectDatabase_TextChanged(object sender, EventArgs e)
        {
            connectionStringBuilder.InitialCatalog = cboConnectDatabase.Text;
            CheckCanConnectionClickNext();
        }

        #endregion

        #endregion

        /// <summary>
        /// Event handler for the wizard changing pages
        /// </summary>
        /// <param name="sender">WizardForm calling this method</param>
        /// <param name="e">EventNextArgs with information about the event</param>
        private void WizardForm1_PageShown(object sender, EventNextArgs e)
        {
            switch (e.Step)
            {
                case 0:
                    wizardForm1.WizardButtons = WizardsButtons.AllShort;
                    break;
                case 1:
                    LoadConnectionPage();
                    break;
                case 2:
                    SaveCurrentConnectionOptions();
                    LoadPreparationPage();
                    break;
            }
        }
    }
}